package com.wpf.system.security.filter;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.wpf.system.common.exception.CodeException;
import com.wpf.system.common.exception.CustomerAuthenionException;
import com.wpf.system.common.utils.JWTUtils;
import com.wpf.system.common.utils.RedisUtils;
import com.wpf.system.security.handler.LoginFailureHandler;
import com.wpf.system.service.impl.UserDetailServiceImpl;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Data
@Component
public class AuthenticationFilter extends OncePerRequestFilter {
    @Value("${system.loginUrl}")
    private String loginUrl;

    @Value("${system.forgetPWDUrl}")
    private String forgetPWDUrl;

    @Value("${system.resetPWUrl}")
    private String resetPWUrl;

    @Value("${system.codeUrl}")
    private String codeUrl;

    @Value("${system.emailCodeUrl}")
    private String emailCodeUrl;

    @Value("${system.checkEmailCodeUrl}")
    private String checkEmailCodeUrl;

    @Resource
    private JWTUtils jwtUtils;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private UserDetailServiceImpl userDetailService;
    @Resource
    private LoginFailureHandler loginFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取请求的url
        String url = request.getRequestURI();
        if(!url.contains("/upload/")){
            try {
                //验证码验证
                if(url.equals(loginUrl)){
                    validateCode(request);
                }
                //token验证
                if(!url.equals(loginUrl)&&!url.equals(codeUrl)
                        &&!url.equals(forgetPWDUrl)&&!url.equals(resetPWUrl)
                        &&!url.equals(emailCodeUrl)&&!url.equals(checkEmailCodeUrl)
                ){
                    validateToken(request);
                }
            }catch (AuthenticationException e){
                loginFailureHandler.onAuthenticationFailure(request,response,e);
                return;
            }
        }
        filterChain.doFilter(request,response);
    }

    private void validateCode(HttpServletRequest request){
        String code = request.getParameter("code");
        if(StringUtils.isEmpty(code)){
            throw new CodeException("验证码不能为空!");
        }else {
            String ip = request.getRemoteAddr();
            String key = ip+code;
            String rightCode =  redisUtils.get(key);
            if(!code.equals(rightCode)){
                throw new CodeException("验证码错误！");
            }
        }
    }

    //token验证
    private void validateToken(HttpServletRequest request){
        //从请求的头部获取token
        String token = request.getHeader("token");
        //如果请求头部没有获取到token，则从请求参数中获取token
        if(StringUtils.isEmpty(token)){
            token = request.getParameter("token");
        }
        if(StringUtils.isEmpty(token)){
            throw new CustomerAuthenionException("token不存在！");
        }
        //看redis里面是否有该token
        String tokenkey = "token_"+token;
        String redisToken = redisUtils.get(tokenkey);
        //如果redis里面没有token,说明该token失效
        if(StringUtils.isEmpty(redisToken)){
            throw new CustomerAuthenionException("token过期！");
        }
        if(!token.equals(redisToken)){
            throw new CustomerAuthenionException("token验证失败！");
        }
        //解析token
        String nid = jwtUtils.getUsernameFromToken(token);
        if(StringUtils.isEmpty(nid)){
            throw new CustomerAuthenionException("token解析失败！");
        }
        //获取用户信息
        UserDetails user = userDetailService.loadUserByUsername(nid);
        if(user == null){
            throw new CustomerAuthenionException("token验证失败！");
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        //设置到spring security上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}
